﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Markup;
using System.Windows.Media;
using System.Collections;

namespace AEA.WPFControls
{
    /// <summary>
    /// Represents a combination of an SplitButton on the left and a drop-down button on the right.
    /// </summary>
    [TemplatePart(Name = SplitButton.ElementComboBox, Type = typeof(Selector))]
    [TemplatePart(Name = SplitButton.ElementButton, Type = typeof(ButtonBase))]
    [TemplatePart(Name = SplitButton.ElementImage, Type = typeof(Image))]
    [TemplatePart(Name = SplitButton.ElementContentPresenter, Type = typeof(ContentPresenter))]
    [ContentProperty("Items")]
    public class SplitButton : Control
    {
        #region Constants
        /// <summary>
        /// Constant for the ComboBox element in the SplitButton control.
        /// </summary>
        private const string ElementComboBox = "PART_ComboBox";
        /// <summary>
        /// Constant for the Button element in the SplitButton control.
        /// </summary>
        private const string ElementButton = "PART_Button";
        /// <summary>
        /// Constant for the Image element in the SplitButton control.
        /// </summary>
        private const string ElementImage = "PART_Image";
        /// <summary>
        /// Constant for the ContentPresenter element in the SplitButton control.
        /// </summary>
        private const string ElementContentPresenter = "PART_Content";
        #endregion

        #region Data
        private Selector _dropDown = null;
        private ButtonBase _button = null;
        private Image _image = null;
        private ContentPresenter _contentPresenter = null;
        private List<SplitButtonItem> _splitButtonItems = null;
        private object _content = null;
        #endregion

        /// <summary>
        /// Initialize a new instance of the <see cref="AEA.WPFControls.SplitButton"/> class.
        /// </summary>
        static SplitButton()
        {
            // Provide a default set of visuals for a custom control
            DefaultStyleKeyProperty.OverrideMetadata(typeof(SplitButton), new FrameworkPropertyMetadata(typeof(SplitButton)));
        }

        #region Public Properties
        #region Items
        /// <summary>
        /// Gets an object representing the collection of the items contained in the items control.
        /// </summary>
        [Category("Common Properties")]
        [Description(" A list to display when the item is clicked.")]
        public List<SplitButtonItem> Items
        {
            get
            {
                if (_splitButtonItems == null)
                {
                    _splitButtonItems = new List<SplitButtonItem>();
                }

                return _splitButtonItems;
            }
        }
        #endregion

        #region SelectionBoxItem
        /// <summary>
        /// Gets the item that is displayed in the selection box. This is a dependency property.
        /// </summary>
        [ReadOnly(true)]
        public object SelectionBoxItem
        {
            get { return GetValue(SelectionBoxItemProperty); }
            private set { SetValue(SelectionBoxItemProperty, value); }
        }

        /// <summary>
        /// Identifies the SelectionBoxItem dependency property.
        /// </summary>
        public static readonly DependencyProperty SelectionBoxItemProperty =
            DependencyProperty.Register(
            "SelectionBoxItem",
            typeof(object),
            typeof(SplitButton));
        #endregion

        #region ClickEvent
        /// <summary>
        /// Event routing for ClickEvent.
        /// </summary>
        public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent
            (
                "Click",
                RoutingStrategy.Bubble,
                typeof(RoutedEventHandler),
                typeof(SplitButton
            ));

        /// <summary>
        /// Occurs when the button portion of the <see cref="AEA.WPFControls.SplitButton"/> is clicked.
        /// </summary>
        public event RoutedEventHandler Click
        {
            add { AddHandler(ClickEvent, value); }
            remove { RemoveHandler(ClickEvent, value); }
        }
        #endregion

        #region Source
        /// <summary>
        /// Gets or sets the ImageSource for the <see cref="AEA.WPFControls.SplitButton"/> image. This is a dependency property.
        /// </summary>
        [Description("Gets or sets the ImageSource for the image.")]
        [Category("Common Properties")]
        public ImageSource Source
        {
            get { return (ImageSource)GetValue(SourceProperty); }
            set { SetValue(SourceProperty, value); }
        }

        /// <summary>
        /// Identifies the Source dependency property.
        /// </summary>
        public static readonly DependencyProperty SourceProperty =
            Image.SourceProperty.AddOwner(
            typeof(SplitButton),
            new PropertyMetadata(null, OnSourcePropertyChanged));

        /// <summary>
        /// SourceProperty property changed handler.
        /// </summary>
        /// <param name="d">SplitButton that changed its Source.</param>
        /// <param name="e">DependencyPropertyChangedEventArgs.</param>
        public static void OnSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            SplitButton sb = d as SplitButton;
            ImageSource imageSource = (ImageSource)e.NewValue;

            if (sb.Source != null)
            {
                if (sb._image == null)
                    return;

                sb._image.Source = imageSource;
                sb.IsSourceEmpty = false;
            }
            else
            {
                sb.IsSourceEmpty = true;
            }
        }
        #endregion

        #region IsSourceEmpty
        /// <summary>
        /// Gets a value that indicates if the SplitButton Source is empty or not. This is a dependency property.
        /// </summary>
        [Description("Gets a value that indicates if the SplitButton Source is empty or not.")]
        [Category("Common Properties")]
        [ReadOnly(true)]
        public bool IsSourceEmpty
        {
            get { return (bool)GetValue(IsSourceEmptyProperty); }
            private set { SetValue(IsSourceEmptyProperty, value); }
        }

        /// <summary>
        /// Identifies the <see cref="AEA.WPFControls.SplitButton.IsSourceEmpty"/> dependency property.
        /// </summary>
        private static readonly DependencyProperty IsSourceEmptyProperty =
            DependencyProperty.Register(
                "IsSourceEmpty",
                typeof(bool),
                typeof(SplitButton),
                new PropertyMetadata(true));
        #endregion

        #region TextImageRelation
        /// <summary>
        /// Gets or sets how the image should be laid out. This is a dependency property.
        /// </summary>
        [Description("Gets or sets how the image should be layed out.")]
        [Category("Layout")]
        public SplitButtonTextImageRelation TextImageRelation
        {
            get { return (SplitButtonTextImageRelation)GetValue(TextImageRelationProperty); }
            set { SetValue(TextImageRelationProperty, value); }
        }

        /// <summary>
        /// Identifies the <see cref="AEA.WPFControls.SplitButton.TextImageRelation"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty TextImageRelationProperty =
            DependencyProperty.Register(
                "TextImageRelation",
                typeof(SplitButtonTextImageRelation),
                typeof(SplitButton),
                new PropertyMetadata(SplitButtonTextImageRelation.ImageBeforeText, OnTextImageRelationPropertyChanged));

        /// <summary>
        /// ImageLayoutProperty property changed handler.
        /// </summary>
        /// <param name="d">SplitButton that changed its ImageLayout.</param>
        /// <param name="e">DependencyPropertyChangedEventArgs.</param>
        public static void OnTextImageRelationPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            SplitButton sb = d as SplitButton;
            SplitButtonTextImageRelation layout = (SplitButtonTextImageRelation)e.NewValue;

            if (layout != SplitButtonTextImageRelation.ImageBeforeText &&
                layout != SplitButtonTextImageRelation.TextBeforeImage &&
                layout != SplitButtonTextImageRelation.ImageAboveText &&
                layout != SplitButtonTextImageRelation.TextAboveImage)
            {
                sb.SetValue(e.Property, e.OldValue);
                throw new ArgumentException(Properties.Resources.SplitButton_OnImageLayoutPropertyChanged_InvalidValue, "value");
            }

            sb.TextImageRelation = layout;
        }
        #endregion

        #region DisplayStyle
        /// <summary>
        /// Specifies whether the image and text are rendered. This is a dependency property.
        /// </summary>
        [Description("Specifies whether the image and text are rendered.")]
        [Category("Common Properties")]
        public SplitButtonDisplayStyle DisplayStyle
        {
            get { return (SplitButtonDisplayStyle)GetValue(DisplayStyleProperty); }
            set { SetValue(DisplayStyleProperty, value); }
        }

        /// <summary>
        /// Identifies the <see cref="AEA.WPFControls.SplitButton.DisplayStyle"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty DisplayStyleProperty =
            DependencyProperty.Register(
                "DisplayStyle",
                typeof(SplitButtonDisplayStyle),
                typeof(SplitButton),
                new PropertyMetadata(SplitButtonDisplayStyle.Text, OnDisplayStylePropertyChanged));

        /// <summary>
        /// DisplayStyleProperty property changed handler.
        /// </summary>
        /// <param name="d">SplitButton that changed its DisplayStyle.</param>
        /// <param name="e">DependencyPropertyChangedEventArgs.</param>
        public static void OnDisplayStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            SplitButton sb = d as SplitButton;
            SplitButtonDisplayStyle displayStyle = (SplitButtonDisplayStyle)e.NewValue;

            if (displayStyle != SplitButtonDisplayStyle.Image &&
                displayStyle != SplitButtonDisplayStyle.Text &&
                displayStyle != SplitButtonDisplayStyle.TextAndImage)
            {
                sb.SetValue(e.Property, e.OldValue);
                throw new ArgumentException(Properties.Resources.SplitButton_OnDisplayStylePropertyChanged_InvalidValue, "value");
            }

            sb.DisplayStyle = displayStyle;
        }
        #endregion

        #region Content
        /// <summary>
        /// Gets or sets the data used to generate the child elements of a System.Windows.Controls.ContentPresenter.
        /// This is a dependency property.
        /// </summary>
        [Description("The Content property of this element can be set to any arbritary value (including simple string values.")]
        [Category("Common Properties")]
        private object Content
        {
            get { return GetValue(ContentProperty); }
            set { SetValue(ContentProperty, value); }
        }

        /// <summary>
        /// Identifies the Content dependency property.
        /// </summary>
        private static readonly DependencyProperty ContentProperty =
            DependencyProperty.Register(
            "Content",
            typeof(object),
            typeof(SplitButton),
            new PropertyMetadata(null, OnContentPropertyChanged));

        /// <summary>
        /// ContentProperty property changed handler.
        /// </summary>
        /// <param name="d">SplitButton that changed its Content.</param>
        /// <param name="e">DependencyPropertyChangedEventArgs.</param>
        private static void OnContentPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            SplitButton sb = d as SplitButton;
            object content = e.NewValue;

            if (sb.Content != null)
            {
                if (sb._contentPresenter == null)
                    return;

                if (sb.Items.Count > 0)
                {
                    sb._contentPresenter.Content = (sb.SelectionBoxItem as SplitButtonItem).Content;
                }
                else
                {
                    sb._contentPresenter.Content = content;
                }
            }
            else
            {
                sb._content = content;
            }
        }
        #endregion

        #region IsDropDownOpen
        /// <summary>
        /// Gets or sets a value indicating whether the drop-down for the SplitButton is currently open. This is a dependency property.
        /// </summary>
        [Description("Gets or sets a value indicating whether the drop-down for the SplitButton is currently open.")]
        [Category("Common Properties")]
        public bool IsDropDownOpen
        {
            get { return (bool)GetValue(IsDropDownOpenProperty); }
            set { SetValue(IsDropDownOpenProperty, value); }
        }

        /// <summary>
        /// Identifies the IsDropDownOpen dependency property.
        /// </summary>
        public static readonly DependencyProperty IsDropDownOpenProperty =
            ComboBox.IsDropDownOpenProperty.AddOwner(
            typeof(SplitButton),
            new PropertyMetadata(false, OnIsDropDownOpenPropertyChanged));

        /// <summary>
        /// IsDropDownOpenProperty property changed handler.
        /// </summary>
        /// <param name="d">SplitButton that changed its IsDropDownOpen.</param>
        /// <param name="e">DependencyPropertyChangedEventArgs.</param>
        public static void OnIsDropDownOpenPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            SplitButton sb = d as SplitButton;
            bool? dropDownOpen = (bool)e.NewValue;

            if (sb._dropDown != null && dropDownOpen != null)
            {
                (sb._dropDown as ComboBox).IsDropDownOpen = (bool)dropDownOpen;
            }
        }
        #endregion

        #region ItemsSource
        /// <summary>
        /// Gets or sets a collection used to generate the content of the System.Windows.Controls.ItemsControl. This is a dependency property.
        /// </summary>
        [Category("Common Properties")]
        [Description("The SplitButton will attach to this ItemsSource.")]
        public IEnumerable ItemsSource
        {
            get { return (IEnumerable)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        /// <summary>
        /// Identifies the ItemsSource dependency property.
        /// </summary>
        public static readonly DependencyProperty ItemsSourceProperty =
            ComboBox.ItemsSourceProperty.AddOwner(
            typeof(SplitButton),
            new PropertyMetadata(null, OnItemsSourcePropertyChanged));

        /// <summary>
        /// ItemsSourceProperty property changed handler.
        /// </summary>
        /// <param name="d">SplitButton that changed its ItemsSource.</param>
        /// <param name="e">DependencyPropertyChangedEventArgs.</param>
        public static void OnItemsSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            SplitButton sb = d as SplitButton;
            IEnumerable ie = (IEnumerable)e.NewValue;

            if (sb._dropDown != null)
            {
                sb._dropDown.ItemsSource = null;
                sb._dropDown.ItemsSource = ie;

            }
        }
        #endregion

        #region MaxDropDownHeight
        /// <summary>
        /// Gets or sets the maximum height for a SplitButton drop-down. This is a dependency property.
        /// </summary>
        [Category("Layout")]
        [Description("Gets or sets the maximum height for a SplitButton drop-down.")]
        public double MaxDropDownHeight
        {
            get { return (double)GetValue(MaxDropDownHeightProperty); }
            set { SetValue(MaxDropDownHeightProperty, value); }
        }

        /// <summary>
        /// Identifies the MaxDropDownHeight dependency property.
        /// </summary>
        public static readonly DependencyProperty MaxDropDownHeightProperty =
            ComboBox.MaxDropDownHeightProperty.AddOwner(
            typeof(SplitButton),
            new PropertyMetadata(300.00, OnMaxDropDownPropertyChanged));

        /// <summary>
        /// MaxDropDownHeightProperty property changed handler.
        /// </summary>
        /// <param name="d">SplitButton that changed its MaxDropDownHeight.</param>
        /// <param name="e">DependencyPropertyChangedEventArgs.</param>
        public static void OnMaxDropDownPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            SplitButton sb = d as SplitButton;
            double? dropDownHeight = (double)e.NewValue;

            if (sb._dropDown != null && dropDownHeight != null)
            {
                (sb._dropDown as ComboBox).MaxDropDownHeight = (double)dropDownHeight;
            }
        }
        #endregion
        #endregion

        #region Public Methods
        #region OnApplyTemplate
        /// <summary>
        /// Builds the visual tree for the SplitButton control when a new template is applied.
        /// </summary>
        public override void OnApplyTemplate()
        {
            if (_dropDown != null)
            {
                _dropDown.SelectionChanged -= new SelectionChangedEventHandler(DropDown_SelectionChanged);
                _dropDown.ItemsSource = null;
            }

            if (_button != null)
            {
                _button.Click -= new RoutedEventHandler(Button_Click);
            }

            if (_image != null)
            {
                _image.Source = null;
            }

            if (_contentPresenter != null)
            {
                _contentPresenter.Content = null;
            }

            base.OnApplyTemplate();

            // Obtain the dropdown and create the items
            _dropDown = GetTemplateChild(SplitButton.ElementComboBox) as Selector;
            _button = GetTemplateChild(SplitButton.ElementButton) as ButtonBase;
            _image = GetTemplateChild(SplitButton.ElementImage) as Image;
            _contentPresenter = GetTemplateChild(SplitButton.ElementContentPresenter) as ContentPresenter;

            if (_dropDown != null)
            {
                _dropDown.SelectionChanged += new SelectionChangedEventHandler(DropDown_SelectionChanged);

                if (_dropDown.Items.Count == 0)
                {
                    _dropDown.ItemsSource = Items;
                }
            }

            if (_button != null)
            {
                _button.Click += new RoutedEventHandler(Button_Click);
            }

            if (_image != null)
            {
                _image.Source = Source;
                IsSourceEmpty = false;
            }

            if (_contentPresenter != null)
            {
                _contentPresenter.Content = Content;
            }
        }
        #endregion

        #endregion

        #region Private Methods
        #region DropDown_SelectionChanged
        /// <summary>
        /// Event handler for when there is a change in the selection of the SplitButton.
        /// </summary>
        private void DropDown_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            SelectionBoxItem = _dropDown.SelectedItem;

            // Set the content of the SplitButton to what was selected
            Content = (_dropDown.SelectedItem as SplitButtonItem).Content;
        }
        #endregion

        #region Button_Click
        /// <summary>
        /// Event handler for when the SplitButton is clicked.
        /// </summary>
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            RoutedEventArgs args = new RoutedEventArgs(ClickEvent);
            RaiseEvent(args);
        }
        #endregion

        #endregion
    }
}